import Dep from "./Dep"

export default class Observer {

  static aryPrototype = Object.create(Array.prototype)

  static methods = [
    'push',
    'pop',
    'unshift',
    'shift',
    'splice',
    'reverse',
    'sort'
  ]

  static {
    for (const method of Observer.methods) {
      Observer.def(Observer.aryPrototype, method, function(...args) {
        const result = Array.prototype[method].call(this, ...args)
        let inserted = []
        switch (method) {
          case 'push':
          case 'unshift':
            inserted = args
            break;
          case 'splice':
            inserted = args.slice(2)
            break
        }
        for (const v of inserted) {
          Observer.observe(v)
        }
        this.__ob__.dep.notify()
        // console.log(`监听到调用数组的${method}方法`);
        return result
      })
    }
  }

  constructor(data) {
    this.dep = new Dep()
    Observer.def(data, '__ob__', this)
    if (Array.isArray(data)) {
      Object.setPrototypeOf(data, Observer.aryPrototype)
      for (const v of data) {
        Observer.observe(v)
      }
    } else {
      for (const k of Object.keys(data)) {
        Observer.defineReactive(data, k)
      }
    }
  }

  static def(data, k, v, configurable = false, enumerable = false, writable = false) {
    Object.defineProperty(data, k, {
      configurable,
      enumerable,
      writable,
      value: v
    })
  }

  static defineReactive(data, k, v) {
    if (arguments.length === 2) v = data[k]
    const childOb = Observer.observe(v)
    Object.defineProperty(data, k, {
      configurable: true,
      enumerable: true,
      get() {
        data.__ob__.dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(v)) {
            Observer.dependArray(v)
          }
        }
        return v
      },
      set(newValue) {
        if (newValue === v) return
        // console.log(`监听到${k}的值由${v}修改成${newValue}`);
        v = newValue
        Observer.observe(newValue)
        data.__ob__.dep.notify()
      }
    })
  }

  static observe(data) {
    if (typeof data !== 'object') return
    let ob = null
    if (data.__ob__) {
      ob = data.__ob__
    } else {
      ob = new Observer(data)
    }
    return ob
  }

  static dependArray(arr) {
    for (let e of arr) {
      e && e.__ob__ && e.__ob__.dep.depend()
      if (Array.isArray(e)) {
        Observer.dependArray(e)
      }
    }
  }

}
